Authentication and Permissions
**Referenced Files in This Document** - [worker.js](file://worker.js) - [wrangler.jsonc](file://wrangler.jsonc) - [package.json](file://package.json) - [README.md](file://README.md) - [src/admin/config.yml](file://src/admin/config.yml) - [tina/config.ts](file://tina/config.ts) - [src/alliance-login.njk](file://src/alliance-login.njk) - [src/alliance-members.njk](file://src/alliance-members.njk) - [src/alliance-knowledge.njk](file://src/alliance-knowledge.njk) - [src/_includes/layouts/iaa-base.njk](file://src/_includes/layouts/iaa-base.njk) - [src/_data/allianceMembers.json](file://src/_data/allianceMembers.json) - [cloudflare-pages.toml](file://cloudflare-pages.toml) - [netlify.toml](file://netlify.toml)Table of Contents
- Introduction
- Project Structure
- Core Components
- Architecture Overview
- Detailed Component Analysis
- Dependency Analysis
- Performance Considerations
- Troubleshooting Guide
- Conclusion
- Appendices
Introduction
This document explains the authentication and permission management for the Ace Strategies Prime platform with a focus on:
- GitHub OAuth integration for CMS access control
- The authentication flow from GitHub login to CMS access
- User role management and permission levels
- The relationship between CMS authentication and member portal access
- Security considerations including token management and session handling
- Setup instructions for GitHub OAuth application configuration
- Troubleshooting authentication issues and access problems
- Integration between CMS users and member authentication systems
Project Structure
The platform is a static site generated by Eleventy and served via Cloudflare Workers. Authentication and permissions are enforced by the Worker:
- Member portal authentication (magic link) and CMS OAuth are handled in the Worker
- The CMS is Sveltia CMS integrated at /admin/ with a Git-backed schema
- Member portal pages are protected and gated behind session cookies
graph TB
subgraph "Static Site (Eleventy)"
E["_site (built)"]
end
subgraph "Cloudflare Worker"
W["worker.js"]
KV1["KV: MEMBER_EMAILS"]
KV2["KV: MAGIC_TOKENS"]
end
subgraph "External Services"
GH["GitHub OAuth"]
RS["Resend (Email)"]
end
U["User Browser"] --> |"/alliance/members/*"| W
U --> |"/admin/"| W
W --> |"/api/auth*"| GH
W --> |"/alliance/login/ (POST)"| RS
W --> |"/alliance/verify/"| KV2
W --> |Session check| KV1
W --> |Static assets fallback| E
Diagram sources
- [worker.js:77-321](file://worker.js#L77-L321)
- [wrangler.jsonc:17-35](file://wrangler.jsonc#L17-L35)
- [cloudflare-pages.toml:1-17](file://cloudflare-pages.toml#L1-L17)
Section sources
- [README.md:1-669](file://README.md#L1-L669)
- [package.json:1-32](file://package.json#L1-L32)
- [cloudflare-pages.toml:1-17](file://cloudflare-pages.toml#L1-L17)
Core Components
- Member portal authentication (magic link)
- Email submission endpoint validates membership and issues a one-time token stored in KV
- Verification endpoint exchanges the token for a signed session cookie
- Session cookie is validated on protected routes
- CMS authentication (GitHub OAuth)
- Sveltia CMS uses a hosted OAuth proxy; the Worker exposes legacy endpoints for compatibility
- CMS schema and permissions
- The CMS schema defines editable content collections and media locations
- Access to /admin/ is controlled by Sveltia CMS; the Worker enforces OAuth for that route
Key implementation references:
- Member authentication routes and logic: [worker.js:77-321](file://worker.js#L77-L321)
- CMS OAuth endpoints: [worker.js:180-227](file://worker.js#L180-L227)
- CMS schema (YAML): [src/admin/config.yml:1-774](file://src/admin/config.yml#L1-L774)
- CMS schema (TypeScript): [tina/config.ts:1-331](file://tina/config.ts#L1-L331)
Section sources
- [worker.js:77-321](file://worker.js#L77-L321)
- [src/admin/config.yml:1-774](file://src/admin/config.yml#L1-L774)
- [tina/config.ts:1-331](file://tina/config.ts#L1-L331)
Architecture Overview
The Worker acts as a router and gatekeeper:
- Protected member routes require a valid session cookie
- CMS access uses Sveltia CMS OAuth (hosted proxy)
- Static assets are served from the built site
sequenceDiagram
participant B as "Browser"
participant W as "Worker"
participant RS as "Resend"
participant KV as "KV Namespaces"
B->>W : "POST /alliance/login/" (email)
W->>KV : "Check MEMBER_EMAILS for approval"
alt Approved
W->>KV : "Store token : <hex> -> email (TTL 900s)"
W->>RS : "Send magic link email"
else Not approved
W-->>B : "Redirect to /alliance/login/?sent=1"
end
B->>W : "GET /alliance/verify/?token=<hex>"
W->>KV : "Fetch token : <hex>"
alt Valid
W->>W : "Create signed session token"
W-->>B : "Set HttpOnly; Secure; SameSite=Lax cookie"
W-->>B : "Redirect to /alliance/members/"
else Expired/Invalid
W-->>B : "Redirect to /alliance/login/?error=expired"
end
Diagram sources
- [worker.js:94-177](file://worker.js#L94-L177)
- [wrangler.jsonc:17-35](file://wrangler.jsonc#L17-L35)
Section sources
- [worker.js:94-177](file://worker.js#L94-L177)
- [README.md:425-477](file://README.md#L425-L477)
Detailed Component Analysis
Member Portal Authentication (Magic Link)
- Session cookie name: ace_member_session
- Session duration: 30 days
- Magic link TTL: 15 minutes
- Token storage: KV MAGIC_TOKENS with key pattern token:
- Approved member storage: KV MEMBER_EMAILS with key pattern member:
- Cookie flags: HttpOnly, Secure, SameSite=Lax
flowchart TD
Start(["Request to /alliance/members/*"]) --> ReadCookie["Read ace_member_session"]
ReadCookie --> HasCookie{"Cookie present?"}
HasCookie --> |No| RedirectLogin["Redirect to /alliance/login/?next={path}"]
HasCookie --> |Yes| VerifyToken["Verify signature and expiry"]
VerifyToken --> Valid{"Valid?"}
Valid --> |Yes| Serve["Serve requested page"]
Valid --> |No| RedirectLogin
Diagram sources
- [worker.js:77-91](file://worker.js#L77-L91)
- [worker.js:39-58](file://worker.js#L39-L58)
Section sources
- [worker.js:77-91](file://worker.js#L77-L91)
- [worker.js:39-58](file://worker.js#L39-L58)
- [README.md:455-464](file://README.md#L455-L464)
GitHub OAuth for CMS Access
- Sveltia CMS uses a hosted OAuth proxy; the Worker exposes legacy endpoints for compatibility
- The Worker’s /api/auth and /api/auth/callback endpoints initiate and finalize OAuth with GitHub
- The CMS schema defines editable content collections and media folders
sequenceDiagram
participant Editor as "CMS Editor"
participant CMS as "Sveltia CMS"
participant W as "Worker"
participant GH as "GitHub"
Editor->>CMS : "Open /admin/"
CMS->>W : "GET /api/auth"
W->>GH : "Redirect to GitHub authorize"
GH-->>W : "Callback with code"
W-->>CMS : "Return access token via postMessage"
CMS-->>Editor : "Load CMS with token"
Diagram sources
- [worker.js:180-227](file://worker.js#L180-L227)
- [src/admin/config.yml:1-5](file://src/admin/config.yml#L1-L5)
- [tina/config.ts:5-21](file://tina/config.ts#L5-L21)
Section sources
- [worker.js:180-227](file://worker.js#L180-L227)
- [src/admin/config.yml:1-5](file://src/admin/config.yml#L1-L5)
- [tina/config.ts:5-21](file://tina/config.ts#L5-L21)
- [README.md:174-204](file://README.md#L174-L204)
CMS Schema and Permission Levels
- The CMS schema defines collections for content and data files
- Media upload location is configured under the CMS schema
- Access to /admin/ is governed by Sveltia CMS; the Worker enforces OAuth for that route
classDiagram
class CMSConfig_YAML {
+collections[]
+media_folder
+public_folder
}
class CMSConfig_TS {
+clientId
+branch
+token
+build.outputFolder
+media.tina.mediaRoot
+schema.collections[]
}
CMSConfig_YAML <.. CMSConfig_TS : "aligned fields"
Diagram sources
- [src/admin/config.yml:1-774](file://src/admin/config.yml#L1-L774)
- [tina/config.ts:1-331](file://tina/config.ts#L1-L331)
Section sources
- [src/admin/config.yml:1-774](file://src/admin/config.yml#L1-L774)
- [tina/config.ts:1-331](file://tina/config.ts#L1-L331)
Member Portal Pages and Protected Routes
- Protected pages include the members dashboard and knowledge base index
- Layouts and content templates define the presentation and navigation
Section sources
- [src/alliance-members.njk:1-58](file://src/alliance-members.njk#L1-L58)
- [src/alliance-knowledge.njk:1-96](file://src/alliance-knowledge.njk#L1-L96)
- [src/_includes/layouts/iaa-base.njk:1-99](file://src/_includes/layouts/iaa-base.njk#L1-L99)
- [src/_data/allianceMembers.json:1-40](file://src/_data/allianceMembers.json#L1-L40)
Dependency Analysis
- Worker depends on:
- KV namespaces MEMBER_EMAILS and MAGIC_TOKENS for membership and tokens
- Resend API key for sending magic link emails
- GitHub OAuth client credentials for legacy CMS OAuth endpoints
- CMS depends on:
- Sveltia CMS hosted OAuth proxy for authentication
- GitHub repository for content storage
graph LR
W["worker.js"] --> |Read/Write| KV1["MEMBER_EMAILS"]
W --> |Read/Write| KV2["MAGIC_TOKENS"]
W --> |POST| RS["Resend API"]
W --> |OAuth| GH["GitHub"]
CMS["Sveltia CMS"] --> |OAuth| W
Diagram sources
- [worker.js:77-321](file://worker.js#L77-L321)
- [wrangler.jsonc:17-35](file://wrangler.jsonc#L17-L35)
Section sources
- [worker.js:77-321](file://worker.js#L77-L321)
- [wrangler.jsonc:17-35](file://wrangler.jsonc#L17-L35)
Performance Considerations
- Static assets are served directly from the built site via the Worker
- KV reads/writes for membership and tokens are minimal and fast
- Email delivery is asynchronous via Resend
- Consider caching for frequently accessed pages and reducing KV round-trips where feasible
[No sources needed since this section provides general guidance]
Troubleshooting Guide
Common issues and resolutions:
- Member authentication
- Session cookie not set or expired: Ensure the cookie flags are accepted by the browser and the domain matches
- Magic link expired: Links are single-use and expire after 15 minutes
- Email not received: Confirm Resend API key is set and the email address is approved
- CMS access
- GitHub OAuth failing: Verify the legacy endpoints are reachable and CORS preflight is handled
- Sveltia CMS login issues: Confirm the hosted OAuth proxy is accessible and the callback URL is correct
- KV configuration
- KV namespaces missing: Create MEMBER_EMAILS and MAGIC_TOKENS namespaces and bind them in the Worker configuration
- Environment variables
- Missing secrets: Ensure SESSION_SECRET, RESEND_API_KEY, and other required secrets are set
Section sources
- [worker.js:77-321](file://worker.js#L77-L321)
- [wrangler.jsonc:17-35](file://wrangler.jsonc#L17-L35)
- [README.md:497-521](file://README.md#L497-L521)
- [README.md:523-548](file://README.md#L523-L548)
Conclusion
The Ace Strategies Prime platform enforces robust authentication and permissions:
- Member portal access is secured via magic link emails and signed session cookies
- CMS access leverages Sveltia CMS with a hosted OAuth proxy, while legacy endpoints remain for compatibility
- KV namespaces and secrets are central to both member authentication and CMS OAuth
- The architecture balances simplicity, security, and maintainability
[No sources needed since this section summarizes without analyzing specific files]
Appendices
Setup Instructions: GitHub OAuth Application Configuration
- Configure Sveltia CMS OAuth proxy (hosted)
- Use the hosted OAuth proxy URL and callback as documented
- Legacy endpoints (if used)
- Set GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET in the Worker secrets
- Ensure /api/auth and /api/auth/callback are reachable and handle CORS preflight
Section sources
- [README.md:174-204](file://README.md#L174-L204)
- [worker.js:180-227](file://worker.js#L180-L227)
- [wrangler.jsonc:28-34](file://wrangler.jsonc#L28-L34)
Setup Instructions: Member Authentication
- Create KV namespaces
- MEMBER_EMAILS: stores approved member emails
- MAGIC_TOKENS: stores one-time tokens with TTL
- Set secrets
- SESSION_SECRET: random 32+ bytes for HMAC signing
- RESEND_API_KEY: for sending magic link emails
- Manage members
- Add/remove approved emails using KV key patterns member:
- Add/remove approved emails using KV key patterns member:
Section sources
- [wrangler.jsonc:17-35](file://wrangler.jsonc#L17-L35)
- [README.md:465-477](file://README.md#L465-L477)
- [README.md:523-548](file://README.md#L523-L548)
Security Considerations
- Token management
- Magic link tokens are single-use and short-lived (15 minutes)
- Session tokens are signed with HMAC-SHA256 and include an expiry
- Session handling
- HttpOnly, Secure, SameSite=Lax flags protect against common attacks
- Session duration is 30 days
- CORS and CSRF
- Legacy OAuth endpoints handle CORS preflight
- Use SameSite=Lax and Secure flags for cookies
- Secrets and KV
- Store secrets via Wrangler; avoid committing to the repository
- Restrict KV keys to the required patterns
Section sources
- [worker.js:180-227](file://worker.js#L180-L227)
- [worker.js:39-58](file://worker.js#L39-L58)
- [worker.js:12-14](file://worker.js#L12-L14)
- [netlify.toml:14-26](file://netlify.toml#L14-L26)